/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2021 Marcus Britanicus (https://gitlab.com/marcusbritanicus)
 * Copyright (c) 2021 Abrar (https://gitlab.com/s96Abrar)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 **/

#include <QDebug>
#include <QWindow>

#include <wayland-client.h>

#include <wayqt/WayQtUtils.hpp>
#include <wayqt/Registry.hpp>
#include <wayqt/LayerShell.hpp>

#include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"

#include "LayerShellImpl.hpp"

void LayerSurfaceImpl::configureCallback( void *data, struct ::zwlr_layer_surface_v1 *, uint32_t serial, uint32_t width, uint32_t height ) {
    auto ls = reinterpret_cast<LayerSurfaceImpl *>(data);

    ls->configureSurface( serial, width, height );
}


void LayerSurfaceImpl::closedCallback( void *data, struct ::zwlr_layer_surface_v1 * ) {
    auto ls = reinterpret_cast<LayerSurfaceImpl *>(data);

    ls->closeSurface();
}


const struct zwlr_layer_surface_v1_listener LayerSurfaceImpl::mLyrSurfListener = {
    configureCallback,
    closedCallback,
};

LayerSurfaceImpl::LayerSurfaceImpl( QtWaylandClient::QWaylandWindow *window, zwlr_layer_surface_v1 *surf, uint version ) : QtWaylandClient::QWaylandShellSurface( window ) {
    mWindow  = window;
    mObj     = surf;
    mVersion = version;

    mAnchors = WQt::LayerSurface::NoAnchor;
    mLyrType = WQt::LayerShell::Top;

    zwlr_layer_surface_v1_add_listener( surf, &mLyrSurfListener, this );
    wl_display_roundtrip( WQt::Wayland::display() );
}


LayerSurfaceImpl::~LayerSurfaceImpl() {
    zwlr_layer_surface_v1_destroy( mObj );
}


void LayerSurfaceImpl::apply() {
    zwlr_layer_surface_v1_set_anchor( mObj, static_cast<uint32_t>(mAnchors) );
    zwlr_layer_surface_v1_set_exclusive_zone( mObj, mExclusiveZone );
    zwlr_layer_surface_v1_set_keyboard_interactivity( mObj, mKeyboardInteractivity );

    if ( mSurfaceSize.isValid() ) {
        zwlr_layer_surface_v1_set_size( mObj, mSurfaceSize.width(), mSurfaceSize.height() );
    }

    zwlr_layer_surface_v1_set_margin( mObj, mMargins.top(), mMargins.right(), mMargins.bottom(), mMargins.left() );

    wl_surface_commit( mWindow->wlSurface() );
    wl_display_roundtrip( WQt::Wayland::display() );
}


void LayerSurfaceImpl::setSurfaceSize( const QSize& surfaceSize ) {
    mSurfaceSize = surfaceSize;
    zwlr_layer_surface_v1_set_size( mObj, mSurfaceSize.width(), mSurfaceSize.height() );
    wl_surface_commit( mWindow->wlSurface() );
}


void LayerSurfaceImpl::setAnchors( const WQt::LayerSurface::SurfaceAnchors& anchors ) {
    mAnchors = anchors;
    zwlr_layer_surface_v1_set_anchor( mObj, (uint32_t)mAnchors );
}


void LayerSurfaceImpl::setExclusiveZone( int exclusiveZone ) {
    mExclusiveZone = exclusiveZone;
    zwlr_layer_surface_v1_set_exclusive_zone( mObj, mExclusiveZone );
}


void LayerSurfaceImpl::setMargins( const QMargins& margins ) {
    mMargins = margins;
    zwlr_layer_surface_v1_set_margin( mObj, mMargins.top(), mMargins.right(), mMargins.bottom(), mMargins.left() );
}


void LayerSurfaceImpl::setKeyboardInteractivity( WQt::LayerSurface::FocusType focusType ) {
    mKeyboardInteractivity = ( uint )focusType;

    /**
     * Protocol version 3
     * Protocol version 3 does not have support for NoFocus/Exclusive/OnDemand enums.
     * It gives us only two choices: 0 and 1.
     */
    if ( mVersion == 3 ) {
        zwlr_layer_surface_v1_set_keyboard_interactivity( mObj, (mKeyboardInteractivity > 0 ? 1 : 0) );
    }

    /**
     * Protocol version 4 or higher
     * Protocol version 4 introduced support for NoFocus/Exclusive/OnDemand enums.
     */
    else {
        zwlr_layer_surface_v1_set_keyboard_interactivity( mObj, mKeyboardInteractivity );
    }
}


void LayerSurfaceImpl::setLayer( WQt::LayerShell::LayerType type ) {
    mLyrType = type;
    zwlr_layer_surface_v1_set_layer( mObj, ( uint )mLyrType );
}


void LayerSurfaceImpl::getPopup( struct ::xdg_popup *popup ) {
    zwlr_layer_surface_v1_get_popup( mObj, popup );
}


zwlr_layer_surface_v1 *LayerSurfaceImpl::get() {
    return mObj;
}


void LayerSurfaceImpl::configureSurface( uint32_t serial, uint32_t width, uint32_t height ) {
    emit configureSize( QSize( width, height ) );

    zwlr_layer_surface_v1_ack_configure( mObj, serial );
}


void LayerSurfaceImpl::closeSurface() {
    emit closeWindow();
}
